package cn.shop.front.util;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;

import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Map;
import java.util.TimeZone;

/**
 * 使用Jackson完成JSON操作 ObjectMapper是线程安全的，可以作为并推荐作为单例使用 注意，把对象转换成json字符串时要保证对象中不存在循环（递归）引用，如A引用B，而B引用同一个A
 * 转换时默认的日期格式是yyyy-MM-dd HH:mm:ss，如果想使用其他格式，需要调用getNewInstance()创建一个新的ObjectMapper对象自己手动设置 值为null的字段不参与生成json格式字符串
 */
public class JsonUtils {

	private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

	private static final JavaType MAP_TYPE = OBJECT_MAPPER.getTypeFactory().constructParametricType(Map.class, String.class, Object.class);

	static {
		// 设置转换时日期格式
		OBJECT_MAPPER.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
		OBJECT_MAPPER.setTimeZone(TimeZone.getTimeZone("GMT+8"));
		// 全局配置序列化返回 JSON 处理
		SimpleModule simpleModule = new SimpleModule();
		//序列化时 将Long类型转为 String  因为前端数字是number 精度为16 而雪花id 为19
		simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
		OBJECT_MAPPER.registerModule(simpleModule);
		// 字段为null时不参与序列化
		OBJECT_MAPPER.setSerializationInclusion(Include.NON_NULL);
		//json或map转bean忽略没有的字段 与@JsonIgnoreProperties(ignoreUnknown = true) 相同
		OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
		OBJECT_MAPPER.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
	}

	/**
	 * 实体类转换为json字符串
	 *
	 * @param obj
	 * @return
	 */
	public static String objectToJson(Object obj) {
		try {
			return OBJECT_MAPPER.writeValueAsString(obj);
		} catch (JsonProcessingException e) {
			throw new RuntimeException(e);
		}
	}


	/**
	 * 字符串集合转换为json字符串 为了避免对list元素中的str再次进行处理从而导致转换结果不可用
	 *
	 * @param strs
	 * @return
	 */
	public static String objectToJson(Collection<String> strs) {
		if (strs == null) {
			return "[]";
		}
		StringBuilder builder = new StringBuilder();
		builder.append("[");
		for (String str : strs) {
			builder.append(str);
			builder.append(",");
		}
		if (strs.size() > 0) {
			builder.deleteCharAt(builder.length() - 1);
		}
		builder.append("]");

		return builder.toString();
	}

	/**
	 * 可以转换普通类型，如User、Card等
	 *
	 * @param jsonStr         json字符串
	 * @param beanClass
	 * @param parametricTypes 泛型的具体类型
	 * @return T
	 */
	@SuppressWarnings("all")
	public static <T> T string2Bean(String jsonStr, Class<T> beanClass) {
		try {
			return OBJECT_MAPPER.readValue(jsonStr, beanClass);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 可以转换为带有泛型的类，如User<Person>等，也可以转换带泛型的类，比如List<T>、Map<K,V>、Xxx<T>等
	 *
	 * @param jsonStr  json字符串
	 * @param javaType 转换后的类型
	 * @return T
	 */
	public static <T> T string2Bean(String jsonStr, JavaType javaType) {
		try {
			return OBJECT_MAPPER.readValue(jsonStr, javaType);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 可以转换为带有泛型的类，如User<Person>等，也可以转换带泛型的类，比如List<T>、Map<K,V>、Xxx<T>等
	 *
	 * @param jsonStr         json字符串
	 * @param beanClass
	 * @param parametricTypes 泛型的具体类型
	 * @return T
	 */
	@SuppressWarnings("all")
	public static <T> T string2Bean(String jsonStr, Class beanClass, Class... parametricTypes) {
		try {
			JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructParametricType(beanClass,
					parametricTypes);
			return OBJECT_MAPPER.readValue(jsonStr, javaType);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 将 Map 转换为 JavaBean
	 *
	 * @param map   被转换的map
	 * @param clazz 类
	 * @return
	 */
	@SuppressWarnings("all")
	public static <T> T map2pojo(Map map, Class<T> clazz) {
		return OBJECT_MAPPER.convertValue(map, clazz);
	}

	/**
	 * 将 Map 转换为 JavaBean
	 *
	 * @param map      被转换的map
	 * @param javaType 类型
	 * @return
	 */
	@SuppressWarnings("all")
	public static <T> T map2pojo(Map map, JavaType javaType) {
		return OBJECT_MAPPER.convertValue(map, javaType);
	}

	/**
	 * 将 Map 转换为 JavaBean
	 *
	 * @param map  被转换的map
	 * @param type 类型
	 * @return
	 */
	@SuppressWarnings("all")
	public static <T> T map2pojo(Map map, Type type) {
		return map2pojo(map, OBJECT_MAPPER.getTypeFactory().constructType(type));
	}

}
